(function(window, undefined) { // private block

var toString = Object.prototype.toString;

var $$ = {
    NotSupportedException : 'NotSupportedException',
    logger : window.console,
    host : 'http://blog-pick.appspot.com', // localhost
    modes : { dev:0, test:1, prod:2 }, mode : null,
    tpls : [],

    ready : function(callback) {
        if (this.mode === null) { this.mode = this.modes.test; }

        this.inc('http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.3/themes/redmond/jquery-ui.css');
        this.inc('http://jii.dyndns.org/widget.css');

        chrome.extension.sendRequest({ action:'init' }, function(result) {
            $$.tpls = result.tpls;
            $(document).ready(function() { setTimeout(callback, 500); });
        });
    },

    inc : function(path, type){
        if (type == 'js' || path.endWith('js')) {
            var script = document.createElement('script');
            script.setAttribute("type","text/javascript");
            script.setAttribute("src", path);
            document.getElementsByTagName("head")[0].appendChild(script);
        }
        else if (type == 'css' || path.endWith('css')) {
            var css = document.createElement("link");
            css.setAttribute("rel", "stylesheet");
            css.setAttribute("type", "text/css");
            css.setAttribute("href", path);
            document.getElementsByTagName("head")[0].appendChild(css);
        }
    },

    id : function(elementId) {
        return $(document.getElementById(elementId));
    },

    url : function(path) {
        if (path.contains('i18n')) { path = path.replace('i18n', i18n.lang); }
        return chrome.extension ? chrome.extension.getURL(path) : '../' + path;
    },

    toArray: function(obj) {
        var result = [];
        for (var i in obj) {
            if (typeof obj[i] != 'function') { result.push(obj[i]) };
        }
        return result;
    },

    get: function(url, data, callback, dataType) {
        this.ajax({ type:'GET', url:url, data:data, success:callback, dataType:dataType });
    },

    post: function(url, data, callback, dataType) {
        this.ajax({ type:'POST', url:url, data:data, success:callback, dataType:dataType });
    },

    ajax: function(args) {
        var dataType = args.dataType;
        args.action = 'ajax';
        args.dataType = 'text';

        this.log('%s %s', args.type, args.url);
        chrome.extension.sendRequest(args, function(result) {
            if (result.status == 'success') {
                if ($.isFunction(args.success)) {
                    if (dataType == 'json') { result.data = JSON.parse(result.data); }
                    if (dataType == 'jsonp') { result.data = eval(result.data); }
                    args.success(result.data, result.status);
                }
            }
            else {
                this.log('Failed to %s %s : %s, %s', args.type, args.url, result.status, result.error);
                if ($.isFunction(args.error)) { args.error(result.status, result.error); }
           }
        });
    },

    tpl: function(id, data, toJQuery) {
        data = data || {};
        var result = this.tpls[id].inject().process(data);
        if (result.contains('[ERROR:')) {
            this.error('Failed to process Template:\n\n' + result); return null;
        }
        return (toJQuery === false) ? result : $(result).inject();
    },

    log : function(msg) {
        if ($$.logger){
            msg = (msg === null) ? 'null' : msg;
            try { $$.logger.log.apply($$.logger, arguments); }
            catch(e) {
                var args = $$.toArray(arguments);
                msg = args.shift()
                $$.logger.log(String.prototype.format.apply(msg, args));
            }
        }
    },

    error : function(msg) {
        this.log.apply(this, arguments);
        if (this.mode != this.modes.prod) {
            alert(msg);
        }
    },

    qunit : {
        url : 'http://localhost/test/qunit.php',
        tests : [],
        ready : function(name, callback, showInPage) {
            if (showInPage) {
                $('body').prepend('<p id="qunit-testresult"></p>');
                $('body').prepend('<ol id="qunit-tests"></ol>');
                $('body').prepend('<h2 id="qunit-userAgent"></h2>');
                $('body').prepend('<div id="qunit-testrunner-toolbar"></div>');
                $('body').prepend('<h2 id="qunit-banner"></h2>');
                $('body').prepend('<h1 id="qunit-header">' + name + '</h1>');
            }

            QUnit.testLog = function(name) {
                $$.qunit.tests.push({ name: name, cases: [] });
            };
            QUnit.log = function(result, message) {
                var idx = $$.qunit.tests.length - 1;
                $$.qunit.tests[idx].cases.push({ result: result, message: message });
            };
            QUnit.done = function(fail, total) {
                var result = '';
                $$.qunit.tests.each(function(theTest) {
                    result += '\n─── 執行 ' + theTest.name + ' 測試 ───\n';
                    theTest.cases.eachWithIndex(function(k, theCase) {
                        theCase.result = theCase.result ? '●' : 'Χ';
                        result += ' <' + (k+1) + '> ' + theCase.result + ' ' + theCase.message + '\n';
                    });
                });
                $$.log(result);

                if (parent) {
                    var html = result.replace(/\n/g, '<br/>').replace(/'/g, "\\'");
                    html = '<pre>' + html + '</pre>';
                    if (showInPage) {
                        html = $('#qunit-tests').html().replace(/\n/g, '<br/>').replace(/'/g, "\\'");
                    }

                    $.getJSON($$.qunit.url, { callback: '?', done: true, total: total, fail: fail, html: html });
                }
                $$.log('\n共執行 ' + total + ' 個測試：' + (total - fail) + ' 個成功，' + fail + ' 個失敗');
            };

            callback();
            QUnit.startup();
        }
    }
};

// Expose $$ to the global object
window.$$ = $$;
window.isset = function(it) {
    return it !== null && it !== undefined;
};

// Don't add functions for Object, this will break some jQuery function

// Add useful functions for String
String.prototype.contains = function(it) { return this.indexOf(it) != -1; };
String.prototype.startWith = function(it) { return this.indexOf(it) === 0; };
String.prototype.endWith = function(it) { return (this.lastIndexOf(it) + it.length) == this.length; };
String.prototype.leftPad = function(len, chr) {
    var str = this;
    while (str.length < len) { str = chr + str; }
    return str;
};
String.prototype.format = function() {
    var result = '';
    var msgs = this.split(/%[sdifo]/);
    for (var i=0; i<msgs.length; i++) {
        result += msgs[i];
        if (i+1 < msgs.length) { result += arguments[i]; }
    }
    return result;
};
String.prototype.inject = function() {
    var result = this;
    var rules = [{ exp1: /(\*\{[\S]+\})/g,   exp2: /\*\{([\S]+)\}/,   transform: $$.url },
                 { exp1: /(\#\{[\w\.]+\})/g, exp2: /\#\{([\w\.]+)\}/, transform: i18n }];

    rules.each(function(rule) {
        if (result.search(rule.exp1) != -1) {
            var temp = '';
            result.split(rule.exp1).each(function(str) {
                if (str.search(rule.exp2) != -1) {
                    temp += rule.transform(str.match(rule.exp2)[1]);
                }
                else {
                    temp += str;
                }
            });
            result = temp;
        }
    });

    return result;
};

// Add useful functions for Array
Array.prototype.each = function(callback) {
    var breaked = false;
    for (var i=0; i<this.length; i++) {
        if (breaked) { break; }
        breaked = (callback(this[i]) === false);
    }
};
Array.prototype.eachWithIndex = function(callback) {
    var breaked = false;
    for (var i=0; i<this.length; i++) {
        if (breaked) { break; }
        breaked = (callback(i, this[i]) === false);
    }
};
Array.prototype.contains = function(target) {
    for (var i=0; i<this.length; i++) {
        if (this[i] == target) {
            return true;
        }
    }
    return false;
};

// Add useful functions for Date
Date.prototype.format = function(incTime) {
    var result = this.getFullYear() + '-';
    result += String(this.getMonth() + 1).leftPad(2, '0') + '-';
    result += String(this.getDate()).leftPad(2, '0');
    if (incTime) {
        result += ' ' + String(this.getHours()).leftPad(2, '0') + ':';
        result += String(this.getMinutes()).leftPad(2, '0') + ':';
        result += String(this.getSeconds()).leftPad(2, '0');
    }
    return result;
};

// Add useful functions for jQuery
jQuery.fn.exists = function() {
    return this.length > 0;
};

jQuery.fn.inject = function() {
    var imgInject = function(it) {
        var src = it.attr('src');
        if (!isset(src)) { return; }
        if (src.startWith('img')) { it.attr('src', $$.url(src)); }
    };

    var exp1 = /(url\([\S]+\))/g, exp2 = /url\(([\S]+)\)/;
    var urlInject = function(it) {
        var style = it.attr('style');
        if (!isset(style)) { return; }
        if (style.search(exp1) == -1) { return; }

        var temp = '';
        style.split(exp1).each(function(str) {
            if (str.search(exp2) != -1) {
                temp += 'url(' + $$.url(str.match(exp2)[1]) + ')';
            }
            else {
                temp += str;
            }
        });
        it.attr('style', temp);
    };

    var i18nInject = function(it) {
        var key = it.attr('i18n');
        if (!isset(key)) { return; }
        it.text(i18n(key));
    };

    imgInject(this);
    urlInject(this);
    i18nInject(this);

    this.find('img').each(function() { imgInject($(this)); });
    this.find('[style]').each(function() { urlInject($(this)); });
    this.find('[i18n]').each(function() { i18nInject($(this)); });

    return this;
};

})(window);